/*
 * Copyright (C) 2022 KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */

#ifndef MODELMANAGER_H
#define MODELMANAGER_H

#include <QObject>
#include <qwindowdefs.h>
#include "base/basemodel.h"
#include "base/categoryitem.h"
#include "base/launcheritem.h"
#include "appmanager_interface.h"

#include "types.h"
#include "tabletdesktopbackend.h"

class ModelManagerPrivate;

typedef BaseModel<LauncherItem *> PageModel;
typedef BaseModel<PageModel *> MultiPageModel;
typedef BaseModel<CategoryItem *> CategoryModel;
class ModelManager : public QObject
{
    Q_OBJECT
    Q_DISABLE_COPY(ModelManager)
    Q_DECLARE_PRIVATE(ModelManager)
    Q_PROPERTY(QAbstractListModel * launcherModel READ launcherModel NOTIFY launcherModelChanged)
    Q_PROPERTY(QAbstractListModel * categoryModel READ categoryModel NOTIFY categoryModelChanged)
    Q_PROPERTY(QAbstractListModel * categoryItemsModel READ categoryItemsModel NOTIFY categoryItemsModelChanged)
    Q_PROPERTY(QAbstractListModel * taskbarModel READ taskbarModel NOTIFY taskbarModelChanged)

public:
    explicit ModelManager(QObject *parent = nullptr);
    ~ModelManager();

    enum ItemParentType {
        Page,
        Group,
        Container,
    };

    ///
    /// \brief launcherModel 获取整个桌面的整体model
    /// \return
    ///
    Q_INVOKABLE QAbstractListModel* launcherModel() const;

    ///
    /// \brief groupModel 获取每个分组（或者Container）的model
    /// \param id 分组或者Container的id
    /// \return
    ///
    Q_INVOKABLE QAbstractListModel* groupModel(const quint32 &id) const;

    ///
    /// \brief categoryModel 返回应用分类的model
    /// \return
    ///
    Q_INVOKABLE QAbstractListModel* categoryModel() const;

    ///
    /// \brief categoryItemsModel 返回所有的分类的对应的item的model
    /// \return
    ///
    Q_INVOKABLE QAbstractListModel* categoryItemsModel() const;

    ///
    /// \brief taskbarModel 获取任务栏的model数据
    /// \return 返回任务栏对应的model
    ///
    Q_INVOKABLE QAbstractListModel* taskbarModel();

    ///
    /// \brief groupPageItemCount 获取分组或者Container指定页的item的数量
    /// \param groupId 分组或者Container的id
    /// \param pageIndex 分组或者Container的页
    /// \return
    ///
    Q_INVOKABLE int groupPageItemCount(int groupId, int pageIndex);

    ///
    /// \brief groupPageCount 获取指定分组的页的总数
    /// \param groupId 分组的id
    /// \return 返回分组内的页的数量
    ///
    Q_INVOKABLE int groupPageCount(int groupId);

    ///
    /// \brief moveItem 移动 item,\note 只适用于同一页面中的 item 移动.
    ///  跨页拖动时, 数据对于 GridView 来说是不同的 model, 因此并不适合 move.
    /// \param from 开始移动的 item 的 index
    /// \param to 要移动到的位置的 item 的 index
    /// \param page 当前移动的 item 所在的页的 index
    /// \param groupId  默认为 -1, 即不是分组内的 item
    /// \todo group->page的移动和page->group的移动
    ///
    Q_INVOKABLE void moveItem(int from,
                              int to,
                              int page,
                              int groupId = -1);

    ///
    /// \brief moveItemToGroup 将主页中的item移动至group分组
    /// \param fromIndex item所在的index
    /// \param fromPage item所在的page index
    /// \param groupId 要放置的group id
    ///
    Q_INVOKABLE void moveItemToGroup(int fromIndex,
                                     int fromPage,
                                     int fromGroupId,
                                     int toGroupId);

    ///
    /// \brief addPlaceholderItem 添加占位 item, 跨页拖动 item 时（未释放）,
    /// 首先在目标 page 添加一个占位 item 来指示将要添加到的位置
    /// \param index 占位 item 所在页的 item index
    /// \param pageIndex 占位 item 所在的 page index
    /// \param rowSpan 占位 item 所跨的行数
    /// \param columnSpan 占位 item 所跨的列数
    /// \param groupId 当前所在的分组 id
    ///
    Q_INVOKABLE void addPlaceholderItem(int index,
                                        int pageIndex,
                                        int rowSpan = 1,
                                        int columnSpan = 1,
                                        int groupId = -1);

    ///
    /// \brief removePlaceholderItem 删除占位 item, 比如从第 0 页拖动 item 至第 1 页,
    /// 此时调用 \memberof addPlaceholderItem, 未释放 item 接着返回第 0 页, 则调用该函数删除占位 item
    ///
    Q_INVOKABLE void removePlaceholderItem();

    ///
    /// \brief replacePlaceholderItem 替换占位 item, 当移动 item 至相应位置释放该 item 时, 调用该函数,
    ///  将拖动的 item 的数据拷贝至占位 item
    /// \param fromIndex 拖动的 item 的 index
    /// \param fromPage 拖动的 item 所在的页
    /// \param groupId 拖动的 item 所在的分组, 默认为 -1, 即不是分组内的 item
    ///
    Q_INVOKABLE void replacePlaceholderItem(int fromIndex,
                                            int fromPage,
                                            int groupId = -1);

    ///
    /// \brief replacePlaceholderItemTaskbar 替换占位 item(占位 item与拖动 item的groupId不同), 当移动 item 至相应位置释放该 item 时, 调用该函数,
    ///  将拖动的 item 的数据拷贝至占位 item
    /// \param fromIndex 拖动的 item 的 index
    /// \param fromPage 拖动的 item 所在的页
    /// \param fromgroupId 拖动的 item 所在的分组, 默认为 -1, 即不是分组内的 item
    /// \param togroupId 占位 item 所在的分组, 默认为 -1
    ///

    Q_INVOKABLE bool replacePlaceholderItemTaskbar(int fromIndex,
                                                   int fromPage,
                                                   int fromgroupId = -1,
                                                   int togroupId= -1);

    ///
    /// \brief removeItem 删除 item, 比如卸载应用完成之后
    /// \param index item 的 index
    /// \param page item 所在页的 index
    /// \param groupId 如果 item 不在 Group 中, 则为 -1
    ///
    Q_INVOKABLE void removeItem(int index,
                                int page,
                                int groupId = -1);

    ///
    /// \brief setItemState 设置item的状态
    /// \param state \sa LauncherItem::ItemState
    /// \param itemId item的id
    ///
    Q_INVOKABLE void setItemState(int state, int itemId);

    ///
    /// \brief setItemsInGroupState 设置分组内的所有item的状态
    /// \param state \sa LauncherItem::ItemState
    /// \param groupId 分组的id
    ///
    Q_INVOKABLE void setItemsInGroupState(int state, int groupId);

    ///
    /// \brief setAllItemsState 设置所有的item的状态
    /// \param state \sa LauncherItem::ItemState
    ///
    Q_INVOKABLE void setAllItemsState(int state);

    ///
    /// \brief popupGroup 弹出分组
    /// \param groupId 分组的id
    ///
    Q_INVOKABLE void popupGroup(int groupId);

    ///
    /// \brief minimizeGroup 最小化分组
    /// \param groupId 分组的id
    ///
    Q_INVOKABLE void minimizeGroup(int groupId);

    ///
    /// \brief createPopupGroupModel 拖动图标至另一图标上面之后动态创建一个分组的model
    /// \param underItemId 拖动悬浮时底下的应用的id
    /// \param aboveItemId 拖动的应用的id，当需要弹出新建分组时为-1，
    ///  如果直接拖拽至应用图标新建分组时则为具体的应用的id
    /// \return 返回新建的分组的id
    ///
    Q_INVOKABLE int createPopupGroupModel(int underItemId,
                                          int aboveItemId,
                                          bool popup);

    ///
    /// \brief setGroupName 设置分组的名称
    /// \param name 分组名称
    /// \param groupId 分组id
    ///
    Q_INVOKABLE void setGroupName(const QString &name, int groupId);

    ///
    /// \brief setAddAppCategory 设置添加应用界面的数据
    /// \param category 默认打卡的分类
    /// \param groupId 添加应用的分组
    ///
    Q_INVOKABLE void setAddAppCategory(int category, int groupId);

    ///
    /// \brief updateCachedData 每次操作完成之后，需要前端主动调用该函数进行缓存数据更新至数据库
    /// \param groupId 当前操作的分组id
    ///
    Q_INVOKABLE void updateCachedData(int groupId = -1);

    ///
    /// \brief groupEditable 分组是否可编辑，包括名称、删除和添加应用
    /// \param groupId 分组的id
    /// \return
    ///
    Q_INVOKABLE bool groupEditable(int groupId);

    ///
    /// \brief updateGroupItems 更新分组item的数据信息，主要用于添加应用界面的数据操作
    ///  每次点击“确认”按钮时需要调用该函数进行数据的更新
    /// \param groupId 当前分组的id
    /// \param pageIndex 打开添加应用界面时所在的page
    ///
    Q_INVOKABLE void updateGroupItems(int groupId, int pageIndex);

    ///
    /// \brief setItemSelectState 用于设置教育大屏项目中“添加应用”界面的应用图标的选中状态
    /// \param itemId item对应的id
    /// \param pageIndex item所在的页
    /// \param selected 是否是选中的状态
    ///
    Q_INVOKABLE void setItemSelectState(int itemId,
                                        int pageIndex,
                                        bool selected);

    ///
    /// \brief setGroupState 设置分组的状态
    /// \param state \enum LauncherItem::ImteState
    /// \param groupId
    ///
    Q_INVOKABLE void setGroupState(int state, int groupId);

    ///
    /// \brief setAppOpenState 设置item为打开状态
    /// \param desktopName item的desktopName
    /// \param wid item的wid
    ///
    void setAppOpenState(QString desktopName, WId wid);

    ///
    /// \brief setAppCloseState 设置item为关闭状态
    /// \param desktopName item的desktopName
    ///
    void setAppCloseState(QString desktopName);

    ///
    /// \brief addWindowIconToTaskbar 打开一个桌面应用，将桌面item图标加入任务栏
    /// \param iconPath item的图标路径
    /// \param appName item的name
    /// \param desktopName item的desktopName
    /// \param wid item的wid
    ///
    void addWindowIconToTaskbar(const QString &iconPath, const QString &desktopName, const QString &appName, const int &wid);

    ///
    /// \brief removeAppIconFromTaskBar 关闭或卸载一个应用，将桌面item图标从任务栏移除
    /// \param desktopFileName item的desktopFileName
    ///
    void removeAppIconFromTaskBar(const QString &desktopFileName);

    ///
    /// \brief findIconPath 根据desktopName查找item的图标路径
    /// \param desktopName item的desktopName
    ///
    QString findIconPath(const QString &desktopName);

    ///
    /// \brief findDesktopName 根据desktopName从桌面查找完整的desktop路径
    /// \param desktopName item的desktopName
    ///
    QString findDesktopName(const QString &desktopName);

    ///
    /// \brief findAppName 根据desktopName从桌面查找应用名称
    /// \param desktopName item的desktopName
    ///
    QString findAppName(const QString &desktopName);

    ///
    /// \brief findDesktopNameFromTaskbar 根据desktopName从任务栏查找完整的desktop路径
    /// \param desktopName item的desktopName
    ///
    QString findDesktopNameFromTaskbar(const QString &desktopName);

    ///
    /// \brief activeWindow 激活窗口
    /// \param wid item的wid
    ///
    Q_INVOKABLE void activeWindow(const quint32 &wid);

    ///
    /// \brief changeAppWindowWid 修改item保存的wid
    /// \param desktopName item被移除的wid
    /// \param newWid item保存的wid
    ///
    void changeAppWindowWid(const QString &desktopName,
                            const int &wid,
                            const int &newWid);

    ///
    /// \brief itemExistsOnDesktop 查询该应用是否在桌面的pageIndex页
    /// \param itemId item的id
    /// \param pageIndex item的pageIndex
    /// \return
    ///
    Q_INVOKABLE bool itemExistsOnDesktop(const int &itemId, const int &pageIndex);
    Q_INVOKABLE int containerCategory(const int containerId);

Q_SIGNALS:
    void desktopDataInited();
    void launcherModelChanged();
    void categoryModelChanged();
    void categoryItemsModelChanged();
    void categoryRemoved(int category);
    void categoryAdded(int category);
    void categoryPageAdded(int category);
    void categoryPageRemoved(int category);
    void taskbarModelChanged();
    void groupNameChanged(const QString &title, int groupId);
    void updateGroup(int groupId, const QList<QList<int> > &ids);
    void updateHomeScreenPage(const QList<QList<int>> &itemIds);
    void createGroup(int groupId,
                     const QString &groupName,
                     const QList<int> &itemIdList);
    void removeGroup(quint32 groupId);
    void uninsatllApp(const QString &desktopFile);
    void replaceGroupIdWithAppId(int groupId, int appId);
    void itemStateChanged(int state);

private Q_SLOTS:
    void initDesktopData(const QMap<int, QList<int>> &pageData,
                     const QMap<int, QList<QList<int>>> &setData,
                     const QMap<int, UKUI::BaseItem *> &itemData);

    void categories(const QMap<UKUI::BaseItem::Categories, QList<UKUI::ItemId>> categories);

    void removeItemFromGroup(int itemId, int groupId);
    void removeItemWithoutGroup(int itemId, int pageIndex);
    void itemInstalled(int itemId,
                       int indexOfAllCategory,
                       int indexOfCategory,
                       int page = -1,
                       bool newCategory = false,
                       int category = CategoryItem::All);
    void iconThemeChanged();
    void changeAppCategory(const quint32 itemId, const QList<int> newCategory);
    void onTaskbarAppIdPosExchanged(int fromIndex, int toIndex);
    void onItemIdMovedToTaskbar(const int itemId, const int insertPosIndex);
    void onItemIdTakedFromTaskbar(const int itemId);
    void onItemIdTakedFromDesktop(const int itemId, const int pageIndex);
    void onItemIdTakedFromGroup(int itemId, int groupId);

private:
    ModelManagerPrivate *d_ptr;
};

#endif // MODELMANAGER_H
